using System;
using System.Collections.Generic;
using System.Web;
using log4net.Appender;
using log4net.Core;
using log4net.Repository.Hierarchy;

namespace Crumbs
{
  public class Profiler : IHttpModule
  {
    public const string DEBUG_INFO_KEY = "Crumbs.Profiler.RequestData";
    public const string DEBUG_TIMESTAMP_KEY = "Crumbs.Profiler.TimeStamp";
    public const string DEBUG_INFO_PLACEHOLDER = "<Crumbs.Profiler.RequestData.Placeholder>";

    private HttpApplication m_context = null;
    private MemoryAppender m_appender;
    private static bool s_enabled = true;

    public void Init(HttpApplication context)
    {
      //if (HttpContext.Current.IsDebuggingEnabled)

      m_context = context;
      AttachHandlers();

      log4net.Config.BasicConfigurator.Configure();
      m_appender = new MemoryAppender();
      m_appender.Layout = new log4net.Layout.PatternLayout("%d [%t] %-5p %c - %m%n");

      ILogger log = log4net.LogManager.GetRepository().GetLogger("NHibernate.SQL");
      Logger logger = (Logger) log;
      logger.Level = Level.All;
      logger.Additivity = false;
      logger.AddAppender(m_appender);
    }

    private void OnEndRequest(object sender, EventArgs e)
    {
      // JSON/XML??
    }

    private void OnBeginRequest(object sender, EventArgs e)
    {
      if(Enabled) m_context.Context.Items[DEBUG_TIMESTAMP_KEY] = DateTime.Now;
    }

    private void OnPreRequestHandlerExecute(object sender, EventArgs e)
    {
      if (!Enabled || ShouldIgnore())
      {
        return;
      }

      GetAppender().Clear();
    }

    private void OnPostRequestHandlerExecute(object sender, EventArgs e)
    {
      if (!Enabled || ShouldIgnore())
        return;

      int selects = 0, updates = 0, deletes = 0, inserts = 0;
      LoggingEvent[] events = GetAppender().GetEvents();

      RequestDebugInfo info = new RequestDebugInfo(m_context.Request.Url.AbsolutePath);
      foreach (LoggingEvent loggingEvent in events)
      {
        string detail = loggingEvent.RenderedMessage.TrimStart(' ', '\n', '\r').ToLowerInvariant();
        string scope = loggingEvent.LoggerName;
        if (scope == "NHibernate.SQL")
        {
          if (detail.StartsWith("select ") || detail.StartsWith("select\n"))
            selects++;
          if (detail.StartsWith("update ") || detail.StartsWith("update\n"))
            updates++;
          if (detail.StartsWith("delete ") || detail.StartsWith("delete\n"))
            deletes++;
          if (detail.StartsWith("insert ") || detail.StartsWith("insert\n"))
            inserts++;
          detail = detail
            .Replace("select ", "<b>SELECT </b>")
            .Replace(" from ", "<b> FROM </b> ")
            .Replace(" left ", "<b> LEFT </b> ")
            .Replace(" right ", "<b> RIGHT </b> ")
            .Replace(" inner ", "<b> INNER </b> ")
            .Replace(" outer ", "<b> OUTER </b> ")
            .Replace(" join ", "<b> JOIN </b> ")
            .Replace(" where ", "<b> WHERE </b> ")
            .Replace("update ", "<b>UPDATE </b> ")
            .Replace(" set ", "<b> SET </b> ")
            .Replace("insert ", "<b>INSERT </b> ")
            .Replace(" into ", "<b> INTO </b> ")
            .Replace(" values ", "<b> VALUES </b> ")
         .Replace(" group by ", "<b> GROUP BY </b>")
         .Replace(Environment.NewLine, "<br/>");
        }

        info.Details.Add(new DebugInfoDetail(loggingEvent.TimeStamp, detail, scope));
      }

      info.Summary = string.Format("{0} selects | {1} updates | {2} inserts | {3} deletes",
                          selects, updates, inserts, deletes);

      List<RequestDebugInfo> infos = (List<RequestDebugInfo>) m_context.Context.Session[DEBUG_INFO_KEY];
      if (infos == null) // possible during request when the debugging is turned on
      {
        infos = new List<RequestDebugInfo>(10);
        m_context.Context.Session[DEBUG_INFO_KEY] = infos;
      }

    	DateTime dtStart = DateTime.Now;

	   // if debugging was turned during the request
		if (m_context.Context.Items[DEBUG_TIMESTAMP_KEY] != null)
			dtStart = (DateTime) m_context.Context.Items[DEBUG_TIMESTAMP_KEY];

      TimeSpan span = DateTime.Now - dtStart;
      info.Duration = (int) span.TotalMilliseconds;

      infos.Add(info);

      if (!m_context.Response.IsRequestBeingRedirected)
      {
        m_context.Context.Session.Remove(DEBUG_INFO_KEY);
        m_context.Response.Filter = new DbgInfoFilter(m_context.Response.Filter, infos);
      }
    }

    public void Dispose()
    {
    }

    MemoryAppender GetAppender()
    {
      return m_appender;
    }

    private bool ShouldIgnore()
    {
      // XML/JSON??
      if (m_context.Context.Response.ContentType != "text/html")
        return true;
      if (!m_context.Context.Response.IsRequestBeingRedirected && m_context.Context.Response.StatusCode != 200)
        return true;

      string name = m_context.Context.CurrentHandler.GetType().Name;
      return name == "HttpForbiddenHandler" || name == "DefaultHttpHandler";
    }

    public static bool Enabled
    {
      get { return s_enabled; }
      set { s_enabled = value; }
    }

    private void AttachHandlers()
    {
      m_context.PreRequestHandlerExecute += OnPreRequestHandlerExecute;
      m_context.PostRequestHandlerExecute += OnPostRequestHandlerExecute;
      m_context.EndRequest += OnEndRequest;
      m_context.BeginRequest += OnBeginRequest;
    }
  }
}
